﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Serialization;

namespace IronCow.Rest
{
    public class RestClient : IRestClient
    {
        #region Constants
        private const string UserAgent = "Mozilla/4.0 IronCow API (compatible; MSIE 6.0; Windows NT 5.1)";
        private const string ApiUrl = "http://api.rememberthemilk.com/services/rest/";
        private const string SecureApiUrl = "https://api.rememberthemilk.com/services/rest/";
        #endregion

        #region Static Members
        private static XmlSerializer sResponseSerializer = new XmlSerializer(typeof(Response));
        #endregion

        #region Private Members
        private object mThrottlingLock = new object();
        private DateTime mLastRequestTime = DateTime.MinValue;
        #endregion

        #region Public Properties
        public string ApiKey { get; set; }
        public string SharedSecret { get; set; }
        public string AuthToken { get; set; }

        public bool UseHttps { get; set; }
        public int Timeout { get; set; }
        public TimeSpan Throttling { get; set; }

        public IResponseCache Cache { get; set; }

#if !SILVERLIGHT
        public IWebProxy Proxy { get; set; }
#endif
        #endregion

        #region Construction
        public RestClient(string apiKey, string sharedSecret)
            : this(apiKey, sharedSecret, null)
        {
        }

        public RestClient(string apiKey, string sharedSecret, string token)
        {
            if (string.IsNullOrEmpty(apiKey))
                throw new ArgumentNullException("apiKey");

            ApiKey = apiKey;
            SharedSecret = sharedSecret;
            AuthToken = token;

            UseHttps = false;
            Timeout = 5000;
            Throttling = TimeSpan.FromSeconds(1);
        }
        #endregion

        #region Authentication Methods
        public bool CheckToken(string token, out RawAuthentication authentication)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("auth_token", token);

            Response response = GetResponse("rtm.auth.checkToken", parameters, false);
            if (response.Status == ResponseStatus.OK)
            {
                authentication = response.Authentication;
                return true;
            }
            else
            {
                authentication = null;
                if (response.Error.Code == ErrorCode.LoginFailedOrInvalidAuthToken)
                    return false;
                throw new RtmException(response.Error);
            }
        }

        public string GetFrob()
        {
            Response response = GetResponse("rtm.auth.getFrob");
            return response.Frob;
        }

        public string GetToken(string frob, out RawUser user)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("frob", frob);

            Response response = GetResponse("rtm.auth.getToken", parameters);
            user = response.Authentication.User;
            return response.Authentication.Token;
        }
        #endregion

        #region Contacts Methods
        public RawContact[] GetContacts()
        {
            Response response = GetResponse("rtm.contacts.getList");
            if (response.Contacts != null)
                return response.Contacts;
            return new RawContact[0];
        }

        public RawContact AddContact(string contact, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("contact", contact);
            parameters.Add("timeline", timeline.ToString());
            Response response = GetResponse("rtm.contacts.add", parameters);
            return response.Contact;
        }

        public void DeleteContact(int contactId, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("contact_id", contactId.ToString());
            parameters.Add("timeline", timeline.ToString());
            GetResponse("rtm.contacts.delete", parameters);
        }
        #endregion

        #region Groups Methods
        public RawGroup[] GetContactGroups()
        {
            Response response = GetResponse("rtm.groups.getList");
            if (response.Groups != null)
                return response.Groups;
            return new RawGroup[0];
        }

        public RawGroup AddContactGroup(string groupName, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("group", groupName);
            parameters.Add("timeline", timeline.ToString());
            Response response = GetResponse("rtm.groups.add", parameters);
            return response.Group;
        }

        public void DeleteContactGroup(int groupId, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("group_id", groupId.ToString());
            parameters.Add("timeline", timeline.ToString());
            GetResponse("rtm.groups.delete", parameters);
        }

        public void AddContactToContactGroup(int contactId, int groupId, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("group_id", groupId.ToString());
            parameters.Add("contact_id", contactId.ToString());
            GetResponse("rtm.groups.addContact", parameters);
        }

        public void RemoveContactFromContactGroup(int contactId, int groupId, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("group_id", groupId.ToString());
            parameters.Add("contact_id", contactId.ToString());
            GetResponse("rtm.groups.removeContact");
        }
        #endregion

        #region Lists Methods
        public RawList AddList(string listName, int timeline)
        {
            return AddList(listName, null, timeline);
        }

        public RawList AddList(string listName, string filter, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("name", listName);
            if (filter != null)
                parameters.Add("filter", filter);
            Response response = GetResponse("rtm.lists.add", parameters);
            return response.List;
        }

        public RawList ArchiveList(string listId, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            Response response = GetResponse("rtm.lists.archive", parameters);
            return response.List;
        }

        public RawList DeleteList(string listId, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            Response response = GetResponse("rtm.lists.delete", parameters);
            return response.List;
        }

        public RawList[] GetLists()
        {
            Response response = GetResponse("rtm.lists.getList");
            return response.Lists;
        }

        public void SetDefaultList(string listId, int timeline)
        {
			var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            if (listId != null)
                parameters.Add("list_id", listId);
            GetResponse("rtm.lists.setDefaultList", parameters);
        }

        public RawList SetListName(string listId, string name, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("name", name);
            Response response = GetResponse("rtm.lists.setName", parameters);
            return response.List;
        }

        public RawList UnarchiveList(string listId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            Response response = GetResponse("rtm.lists.unarchive", parameters);
            return response.List;
        }
        #endregion

        #region Locations Methods
        public RawLocation[] GetLocations()
        {
            Response response = GetResponse("rtm.locations.getList");
            return response.Locations;
        }
        #endregion

        #region Settings Methods
        public RawSettings GetSettings()
        {
            Response response = GetResponse("rtm.settings.getList");
            return response.Settings;
        }
        #endregion

        #region Tasks Methods
        public RawList AddTask(string name, int timeline)
        {
            return AddTask(name, false, timeline);
        }

        public RawList AddTask(string name, bool parse, int timeline)
        {
            return AddTask(name, parse, null, timeline);
        }

        public RawList AddTask(string name, string listId, int timeline)
        {
            return AddTask(name, false, listId, timeline);
        }

        public RawList AddTask(string name, bool parse, string listId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            if (listId != null)
                parameters.Add("list_id", listId);
            parameters.Add("name", name);
            if (parse)
                parameters.Add("parse", "1");
            Response response = GetResponse("rtm.tasks.add", parameters);
            return response.List;
        }

        public RawList AddTaskTags(string listId, string taskSeriesId, string taskId, string tags, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("tags", tags);
            Response response = GetResponse("rtm.tasks.addTags", parameters);
            return response.List;
        }

        public RawList CompleteTask(string listId, string taskSeriesId, string taskId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            Response response = GetResponse("rtm.tasks.complete", parameters);
            return response.List;
        }

        public RawList DeleteTask(string listId, string taskSeriesId, string taskId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            Response response = GetResponse("rtm.tasks.delete", parameters);
            return response.List;
        }

        public RawList[] GetTasks(string listId, string filter)
        {
            return GetTasks(listId, filter, null);
        }

        public RawList[] GetTasks(string listId, string filter, DateTime? lastSync)
        {
            var parameters = new Dictionary<string, string>();
            if (listId != null) parameters["list_id"] = listId;
            if (filter != null) parameters["filter"] = filter;
            if (lastSync != null) parameters["last_sync"] = lastSync.Value.ToString("s");
            Response response = GetResponse("rtm.tasks.getList", parameters);
            return response.Tasks;
        }

        public RawList MoveTaskPriorityUp(string listId, string taskSeriesId, string taskId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("direction", "up");
            Response response = GetResponse("rtm.tasks.movePriority", parameters);
            return response.List;
        }

        public RawList MoveTaskPriorityDown(string listId, string taskSeriesId, string taskId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("direction", "down");
            Response response = GetResponse("rtm.tasks.movePriority", parameters);
            return response.List;
        }

        public RawList MoveTaskTo(string fromListId, string toListId, string taskSeriesId, string taskId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("from_list_id", fromListId);
            parameters.Add("to_list_id", toListId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            Response response = GetResponse("rtm.tasks.moveTo", parameters);
            return response.List;
        }

        public RawList PostponeTask(string listId, string taskSeriesId, string taskId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            Response response = GetResponse("rtm.tasks.postpone", parameters);
            return response.List;
        }

        public RawList RemoveTaskTags(string listId, string taskSeriesId, string taskId, string tags, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("tags", tags);
            Response response = GetResponse("rtm.tasks.removeTags", parameters);
            return response.List;
        }

        public RawList SetTaskDueDate(string listId, string taskSeriesId, string taskId, string due, int timeline)
        {
            return SetTaskDueDate(listId, taskSeriesId, taskId, due, false, true, timeline);
        }

        public RawList SetTaskDueDate(string listId, string taskSeriesId, string taskId, string due, bool hasDueTime, bool parse, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            if (due != null)
                parameters.Add("due", due);
            if (hasDueTime)
                parameters.Add("has_due_time", "1");
            if (parse)
                parameters.Add("parse", "1");
            Response response = GetResponse("rtm.tasks.setDueDate", parameters);
            return response.List;
        }

        public RawList SetTaskEstimate(string listId, string taskSeriesId, string taskId, string estimate, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("estimate", estimate);
            Response response = GetResponse("rtm.tasks.setEstimate", parameters);
            return response.List;
        }

        public RawList SetTaskLocation(string listId, string taskSeriesId, string taskId, int locationId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("location_id", locationId.ToString());
            Response response = GetResponse("rtm.tasks.setLocation", parameters);
            return response.List;
        }

        public RawList SetTaskName(string listId, string taskSeriesId, string taskId, string name, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("name", name);
            Response response = GetResponse("rtm.tasks.setName", parameters);
            return response.List;
        }

        public RawList SetTaskPriority(string listId, string taskSeriesId, string taskId, int? priority, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            if (priority.HasValue)
                parameters.Add("priority", priority.ToString());
            Response response = GetResponse("rtm.tasks.setPriority", parameters);
            return response.List;
        }

        public RawList ResetTaskPriority(string listId, string taskSeriesId, string taskId, int timeline)
        {
            return SetTaskPriority(listId, taskSeriesId, taskId, null, timeline);
        }

        public RawList SetTaskRecurrence(string listId, string taskSeriesId, string taskId, string recurrence, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("repeat", recurrence);
            Response response = GetResponse("rtm.tasks.setRecurrence", parameters);
            return response.List;
        }

        public RawList SetTaskTags(string listId, string taskSeriesId, string taskId, string tags, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("tags", tags);
            Response response = GetResponse("rtm.tasks.setTags", parameters);
            return response.List;
        }

        public RawList SetTaskUrl(string listId, string taskSeriesId, string taskId, string url, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("url", url);
            Response response = GetResponse("rtm.tasks.setURL", parameters);
            return response.List;
        }

        public RawList UncompleteTask(string listId, string taskSeriesId, string taskId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            Response response = GetResponse("rtm.tasks.uncomplete", parameters);
            return response.List;
        }
        #endregion

        #region Task Notes Methods
        public RawNote AddTaskNote(string listId, string taskSeriesId, string taskId, string noteTitle, string noteText, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("list_id", listId);
            parameters.Add("taskseries_id", taskSeriesId);
            parameters.Add("task_id", taskId);
            parameters.Add("note_title", noteTitle);
            parameters.Add("note_text", noteText);
            Response response = GetResponse("rtm.notes.add", parameters);
            return response.Note;
        }

        public void DeleteTaskNode(int noteId, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("note_id", noteId.ToString());
            GetResponse("rtm.notes.delete", parameters);
        }

        public RawNote EditTaskNote(int noteId, string noteTitle, string noteText, int timeline)
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("timeline", timeline.ToString());
            parameters.Add("note_id", noteId.ToString());
            parameters.Add("note_title", noteTitle);
            parameters.Add("note_text", noteText);
            Response response = GetResponse("rtm.notes.edit", parameters);
            return response.Note;
        }
        #endregion

        #region Test Methods
        public bool TestLogin()
        {
            Response response = GetResponse("rtm.test.login", false);
            if (response.Status == ResponseStatus.OK)
            {
                return true;
            }
            else
            {
                if (response.Error.Code == ErrorCode.UserNotLoggedInOrInsufficientPermissions)
                    return false;
                throw new RtmException(response.Error);
            }
        }
        #endregion

        #region Time Methods
        public DateTime ParseTime(string text, string timezone, TimeFormat timeFormat)
        {
            var parameters = new Dictionary<string, string>();
            parameters["text"] = text;
            parameters["dateFormat"] = ((int)timeFormat).ToString();
            if (!string.IsNullOrEmpty(timezone))
                parameters["timezone"] = timezone;
            Response response = GetResponse("rtm.time.parse", parameters);
            return response.Time.Time;
        }
        #endregion

        #region Timeline Methods
        public int CreateTimeline()
        {
            Response response = GetResponse("rtm.timelines.create");
            return response.Timeline;
        }
        #endregion

        #region Timezone Methods
        public RawTimezone[] GetTimezones()
        {
            Response response = GetResponse("rtm.timezones.getList");
            return response.Timezones;
        }
        #endregion

        #region Response Methods
        public string EscapeRequestParameter(string value)
        {
            value = Uri.EscapeDataString(value);
            return value;
        }

        public Response GetResponse(string method)
        {
            return GetResponse(method, true);
        }

        public Response GetResponse(string method, bool throwOnError)
        {
			var parameters = new Dictionary<string, string>();
            return GetResponse(method, parameters, throwOnError);
        }

        public Response GetResponse(string method, Dictionary<string, string> parameters)
        {
            return GetResponse(method, parameters, true);
        }

		public Response GetResponse(string method, Dictionary<string, string> parameters, bool throwOnError)
        {
            parameters["method"] = method;
            parameters["api_key"] = ApiKey;
            if (AuthToken != null)
                parameters["auth_token"] = AuthToken;

			string[] keys = parameters.Keys.ToArray();
            Array.Sort(keys);

            StringBuilder variablesBuilder = new StringBuilder();

            foreach (string key in keys)
            {
                if (variablesBuilder.Length > 0)
                    variablesBuilder.Append("&");

                variablesBuilder.Append(key);
                variablesBuilder.Append("=");
                variablesBuilder.Append(EscapeRequestParameter(parameters[key]));
            }

            if (SharedSecret != null)
            {
                if (variablesBuilder.Length > 0)
                    variablesBuilder.Append("&");

                string apiSig = RtmAuthentication.GetApiSig(SharedSecret, parameters);
                variablesBuilder.Append("api_sig=");
                variablesBuilder.Append(apiSig);
            }

            string url = UseHttps ? SecureApiUrl : ApiUrl;
            string variables = variablesBuilder.ToString();
            string rawUrl = url + "?" + variables;
            string responseString = null;

            if (Cache != null)
            {
                if (Cache.HasCachedResponse(rawUrl))
                {
                    responseString = Cache.GetCachedResponse(rawUrl);
                    IronCowTraceSource.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Got response for '{0}' from cache.", method);
                }
            }
            if (responseString == null)
            {
                lock (mThrottlingLock)
                {
                    TimeSpan timeSinceLastRequest = DateTime.Now - mLastRequestTime;
                    if (timeSinceLastRequest < Throttling)
                    {
                        TimeSpan wait = Throttling - timeSinceLastRequest;
                        IronCowTraceSource.TraceSource.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 0, "Throttling RTM request for {0}ms.", wait.TotalMilliseconds);
                        System.Threading.Thread.Sleep((int)Math.Ceiling(wait.TotalMilliseconds));
                    }

					DateTime startTime = DateTime.Now;
                    responseString = GetRawResponse(url, variables);
                    mLastRequestTime = DateTime.Now;
					TimeSpan elapsed = mLastRequestTime - startTime;
					IronCowTraceSource.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Got response for '{0}' in {1}ms.", method, elapsed.TotalMilliseconds);
                }

                if (Cache != null)
                    Cache.CacheResponse(rawUrl, responseString);
            }

            StringReader responseReader = new StringReader(responseString);
            Response response = (Response)sResponseSerializer.Deserialize(responseReader);
            responseReader.Close();

            if (throwOnError && response.Status != ResponseStatus.OK)
                throw new RtmException(response.Error);
            return response;
        }

        private string GetRawResponse(string url, string variables)
        {
            if (variables.Length < 2000)
            {
                url += "?" + variables;
                variables = "";
            }

            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
            req.Method = "POST";
            req.UserAgent = UserAgent;
#if !SILVERLIGHT
            req.ContentLength = variables.Length;
            req.Timeout = Timeout;
            req.KeepAlive = false;
            if (Proxy != null)
                req.Proxy = Proxy;

            if (variables.Length > 0)
            {
                req.ContentType = "application/x-www-form-urlencoded";
                using (StreamWriter sw = new StreamWriter(req.GetRequestStream()))
                {
                    sw.Write(variables);
                    sw.Close();
                }
            }
#if COMPACT_FRAMEWORK
            else
            {
                // <reallySucking>
                // in the compact framework we need to explicitly close the 
                // request stream because it is not properly closed by the framework
                // itself, even though we don't use an explicit request stream.
                // see http://social.msdn.microsoft.com/Forums/en-US/netfxcompact/thread/65ad4e2a-de0b-4a2d-bec3-6ccad26da0c7/
                req.GetRequestStream().Close();
                // </reallySucking>
            }
#endif
#endif

#if SILVERLIGHT
            using (var waitHandle = new System.Threading.AutoResetEvent(false))
            {
                var state = new GetResponseCallbackState() { WaitHandle = waitHandle, Request = req };
                req.BeginGetResponse(GetReponseCallback, state);
                waitHandle.WaitOne();
                return state.Response;
            }
#else
			string responseString = string.Empty;
            using (WebResponse res = req.GetResponse())
            {
                return GetResponseString(res);
            }
#endif
        }

		private static string GetResponseString(WebResponse response)
		{
			string responseString = string.Empty;
			using (StreamReader sr = new StreamReader(response.GetResponseStream()))
			{
				responseString = sr.ReadToEnd();
				sr.Close();
			}
			return responseString;
		}
        #endregion

		#region Silverlight Members
#if SILVERLIGHT
		private class GetResponseCallbackState
		{
            public System.Threading.EventWaitHandle WaitHandle;
			public HttpWebRequest Request;
			public string Response;
		}

		private static void GetReponseCallback(IAsyncResult ar)
		{
			var state = (GetResponseCallbackState)ar.AsyncState;
			using (WebResponse res = state.Request.EndGetResponse(ar))
			{
				state.Response = GetResponseString(res);
			}
            state.WaitHandle.Set();
		}
#endif
		#endregion
    }
}
